//-----------------------------------------------------------------------------
// Bend2D - Physics Engine
// Roberto Bender <beto.bender@gmail.com>
// Description: Engine Unittests
// Date: 08/03/2010
//-----------------------------------------------------------------------------

#include "point.h"
#include "angle.h"
#include "acceleration.h"
#include "durable.h"
#include "drawable.h"
#include "bmath.h"
#include "extension/colors.h"

#include <cassert>
#include <iostream>
#include <map>
#include <string>
#include <iomanip>

using namespace std;
using namespace bend2d;

#define OK(a) cout << "Success Testing bend2d::" << a << "\n"
#define INIT(a) cout << "Start Testing bend2d::" << a << "\n"
#define TRACE(a,msg) cout << "  bend2d::" << a << " -> " << msg << "\n"
#define TEST_K 0.00001
#define TEST_DOUBLE(A,B) {double Avalue = A; double Bvalue = B; cout << " Testing Double (" << Avalue << "==" << Bvalue << "): " << flush; assert(Avalue-Bvalue >= -TEST_K && Avalue-Bvalue <= TEST_K); cout << "OK\n"; }
#define TEST_UINT(A,B) {unsigned int Avalue = A; unsigned int Bvalue = B; cout << " Testing UInt (" << setfill('0') << hex << setw(8) << Avalue << "==" << setw(8) << hex << Bvalue << "): " << flush; assert(Avalue == Bvalue); cout << "OK\n"; }
#define TEST_BOOL(A,B) {double Avalue = A; double Bvalue = B; cout << " Testing Bool (" << Avalue << "==" << Bvalue << "): " << flush; assert(Avalue == Bvalue); cout << "OK\n"; }
#define TEST_STRING(A,B) {string Avalue = A; string Bvalue = B; cout << " Testing String (" << Avalue << "==" << Bvalue << "): " << flush; assert(Avalue == Bvalue); cout << "OK\n"; }

class DrawableTest : public Drawable
{
    public:
        DrawableTest() : m_Draw(0) {}
        unsigned int m_Draw;
        void Draw() 
        {
            m_Draw++;
        }
};

int main(int argc, char* argv[])
{
    //-----------------------------------------//
    //-----------------------------------------//
    INIT("Point");

    Point pointA;
    Point pointB(5, 10);
    Point pointC(10, 5);
    Point pointD(pointC);

    TEST_DOUBLE(pointA.GetX(), 0);
    TEST_DOUBLE(pointA.GetY(), 0);
    TEST_DOUBLE(pointB.GetX(), 5);
    TEST_DOUBLE(pointB.GetY(), 10);
    TEST_DOUBLE(pointC.GetX(), 10);
    TEST_DOUBLE(pointC.GetY(), 5);
    TEST_DOUBLE(pointD.GetX(), pointC.GetX());
    TEST_DOUBLE(pointD.GetY(), pointC.GetY());

    pointA+=pointB;
    pointB+=pointC;
    pointD = pointD + pointD + pointD;

    TEST_DOUBLE(pointA.GetX(), 5);
    TEST_DOUBLE(pointA.GetY(), 10);
    TEST_DOUBLE(pointB.GetX(), 15);
    TEST_DOUBLE(pointB.GetY(), 15);
    TEST_DOUBLE(pointD.GetX(), 30);
    TEST_DOUBLE(pointD.GetY(), 15);

    Point pointBegin(0, -100);
    Point pointEnd(-100, 0);

    TEST_DOUBLE(pointBegin.GetAngle().GetAngle() - pointEnd.GetAngle().GetAngle(), -270.0);

    Point pointAl;
    pointAl += pointAl;
    TEST_DOUBLE(pointAl.GetX(), 0);
    TEST_DOUBLE(pointAl.GetY(), 0);

    Point pointBl(7,8);
    pointBl += pointAl;
    TEST_DOUBLE(pointBl.GetX(), 7);
    TEST_DOUBLE(pointBl.GetY(), 8);
    OK("Point");

    //-----------------------------------------//
    //-----------------------------------------//
    INIT("Angle");

    Angle angleA(90);
    Angle angleB(Math::pi, Angle::eAngleRad);
    
    TEST_DOUBLE(angleA.GetAngle(), 90);
    TEST_DOUBLE(angleB.GetAngle(), 180);

    TEST_DOUBLE(angleA.GetAngle(Angle::eAngleRad), Math::pi / 2.0);
    TEST_DOUBLE(angleB.GetAngle(Angle::eAngleRad), Math::pi);

    angleA.Rotate(90);
    TEST_DOUBLE(angleA.GetAngle(), 180);
    angleA.Rotate(-90, Angle::eAngleAbsolute);
    TEST_DOUBLE(angleA.GetAngle(), (-90));

    Angle angleC(90);
    Angle angleD(91);

    Angle diffAngle = angleC - angleD;
    TEST_DOUBLE(diffAngle.GetAngle(Angle::eAngleDegreePos), 359);

    OK("Angle");

    //-----------------------------------------//
    //-----------------------------------------//
    INIT("Acceleration");

    Acceleration accA;
    Acceleration accB(0.33);
    Acceleration accC(0.33, angleA);

    TEST_DOUBLE(accA.GetMag(), 0);
    TEST_DOUBLE(accB.GetMag(), 0.33);
    TEST_DOUBLE(accC.GetMag(), 0.33);
    TEST_DOUBLE(accA.GetAngle().GetAngle(), 0);
    TEST_DOUBLE(accB.GetAngle().GetAngle(), 0);
    TEST_DOUBLE(accC.GetAngle().GetAngle(), (-90));

    OK("Acceleration");

    //-----------------------------------------//
    //-----------------------------------------//
    INIT("Drawable");

    DrawableTest drawableTest;
    Drawable* drawableA = &drawableTest;
    TEST_UINT(drawableTest.m_Draw, 0);
    TEST_UINT(drawableTest.m_Draw, 0);
    drawableA->Draw();
    TEST_UINT(drawableTest.m_Draw, 1);
    drawableA->Draw();
    TEST_UINT(drawableTest.m_Draw, 2);

    OK("Drawable");

    //-----------------------------------------//
    //-----------------------------------------//
    INIT("Line");
    Line lineA;
    Line lineB(pointA, pointB);
    Line lineC(0, 1, 2, 3);

    TEST_DOUBLE(lineA.GetFrom().GetX(), 0);
    TEST_DOUBLE(lineA.GetFrom().GetY(), 0);
    TEST_DOUBLE(lineA.GetTo().GetX(), 0);
    TEST_DOUBLE(lineA.GetTo().GetY(), 0);

    TEST_DOUBLE(lineB.GetFrom().GetX(), 5);
    TEST_DOUBLE(lineB.GetFrom().GetY(), 10);
    TEST_DOUBLE(lineB.GetTo().GetX(), 15);
    TEST_DOUBLE(lineB.GetTo().GetY(), 15);

    TEST_DOUBLE(lineC.GetFrom().GetX(), 0);
    TEST_DOUBLE(lineC.GetFrom().GetY(), 1);
    TEST_DOUBLE(lineC.GetTo().GetX(), 2);
    TEST_DOUBLE(lineC.GetTo().GetY(), 3);

    TRACE("Line", "Creating Intersecction Test Map");
    map<string, Line> lines;
    lines["lineA"] = Line(-7,2,-7,-1);
    lines["lineB"] = Line(-6,-3,-6,5);
    lines["lineC"] = Line(-8,-1,-5,2);
    lines["lineD"] = Line(1,3,-3,-3);
    lines["lineE"] = Line(-3,1,-1,-1);
    lines["lineF"] = Line(3,-1,3,2);
    lines["lineG"] = Line(6,0,2,0);
    lines["lineH"] = Line(4,-1,6,1);
    lines["lineI"] = Line(4,-3,5,-1);
    lines["lineJ"] = Line(6,-1,8,3);

    map<string,bool> lineTests;
    lineTests["lineA+lineC"] = true;
    lineTests["lineC+lineA"] = true;
    lineTests["lineB+lineC"] = true;
    lineTests["lineC+lineB"] = true;
    lineTests["lineD+lineE"] = true;
    lineTests["lineE+lineD"] = true;
    lineTests["lineF+lineG"] = true;
    lineTests["lineG+lineF"] = true;
    lineTests["lineG+lineH"] = true;
    lineTests["lineH+lineG"] = true;

    //TRACE("Line", "Testing...");
    for(map<string, Line>::iterator it1 = lines.begin(); it1 != lines.end(); ++it1)
        for(map<string, Line>::iterator it2 = lines.begin(); it2 != lines.end(); ++it2)
        {
            if(it1->first == it2->first)
                continue;

            string test = it1->first + "+" + it2->first;
            //TRACE("Line", "Test: " << test);
            Point point;
            TEST_BOOL(Math::Cross(it1->second, it2->second, point), lineTests[test]);
        }

    OK("Line");

    INIT("Math");
    string convret;
    Math::ConvertToBinary(0xFFFF, 16, convret);
    TEST_STRING("1111111111111111", convret);
    Math::ConvertToBinary(31, 16, convret);
    TEST_STRING("0000000000011111", convret);
    
    OK("Math");

    INIT("Extension::Color");
    Color color1(0xFFFFFFFF);
    Color color2(0xFF00FF00);
    Color color3(0x80706050);

    TEST_UINT(color1.To8888(), 0xFFFFFFFF);
    TEST_UINT(color2.To8888(), 0xFF00FF00);
    TEST_UINT(color3.To8888(), 0x80706050);

    TEST_UINT(color1.To565(), 0xFFFF);

    Color color4(0xFFFF, Color::eSourceType565);
    TEST_UINT(color4.To565(), 0xFFFF);
    Color color5(0x7050, Color::eSourceType565);
    TEST_UINT(color5.To565(), 0x7050);

    TEST_BOOL(color1.Match(color1), true);
    TEST_BOOL(color2.Match(color2, 0), true);
    TEST_BOOL(color3.Match(color3, 0), true);
    TEST_BOOL(color1.Match(color2), false);

    TEST_UINT(color4.To565(Color::eOutputMaskRed565), 0xF800);
    TEST_UINT(color4.To565(Color::eOutputMaskBlue565), 31);

    OK("Extension::Color");
	return 0;
}
